#ifndef __PINFLOW_MACHINE_H__
#define __PINFLOW_MACHINE_H__

#include "task.h"
#include "config.h"

#include <thread>
#include <memory>
#include <string>
#include <vector>
#include <map>

namespace pinflow
{

using WorkLoadQueues = std::map<std::string,
    ThreadSafeList<InOutBase::sptr>::sptr>;

class Machine
{
public:

using sptr = std::shared_ptr<Machine>;

enum STATE
{
    UNKNOWN = 0,
    INITIALIZED = 1,
    RUNNING = 2,
    STOPPED = 3,
};

static sptr Create() {
    return std::shared_ptr<Machine>(new Machine());
}

void SetName(const std::string& name) { name_ = name; }
void SetState(const STATE& state) { state_ = state; }
void SetConfig(const Config& config) { config_ = config; }

bool BindTo(int cpu)
{
    cpu_ = cpu;
    cpu_set_t cpuset;
    CPU_ZERO(&cpuset);
    CPU_SET(cpu, &cpuset);
    if (pthread_setaffinity_np(pthread_self(),
        sizeof(cpu_set_t), &cpuset) != 0) {
        return false;
    }
    return true;
}

bool Init()
{
    if (state_ != RUNNING) return false;
    // start ping main thread

    state_ = INITIALIZED;
    return true;
}

const std::string& Name() const { return name_; }
const Config& ConfigItems() const { return config_; }

bool IsRunning() const {return state_ == RUNNING;}

bool Loop(WorkLoadQueues& queues, bool& flag)
{
    bool ret = true;
    std::thread thread([&](WorkLoadQueues& queues, bool& flag) {
            BindTo(cpu_);
            while (flag) {
                for (size_t i = 0; i < tasks_.size(); ++i) {
                    auto& task = tasks_[i];

                    if (!task->Mask()) continue;

                    // not find
                    if (queues.count(task->InputName()) == 0) {
                        ret = false;
                        break;
                    }
                    if (queues.count(task->OutputName()) == 0) {
                        ret = false;
                        break;
                    }

                    auto& input_queue = queues[task->InputName()];
                    auto& output_queue = queues[task->OutputName()];
                    ret = task->Process(input_queue, output_queue);
                    if (!ret) break;
                }
            }

            state_ = STOPPED;
        }, std::ref(queues), std::ref(flag)
    );

    thread.detach();
    return true;
}

protected:
    Machine(): cpu_(-1), state_(UNKNOWN) {}

private:

    int cpu_;
    std::string name_;
    STATE state_;
    WorkloadType type_;
    std::vector<TaskBase::sptr> tasks_;
    Config config_;
}; // class Machine

} // namespace pinflow

#endif // __PINFLOW_MACHINE_H__